package checkcode;

import configuration.RobConfiguration;
import enumeration.EnumRobChannel;
import http.util.FileUtil;
import main.robClass;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;

import javax.imageio.IIOException;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.SocketException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class ImagePreProcess {
    private static Map<BufferedImage, String> trainMap = null;
    private static int index = 500;

    /*
     * 生成字模库>注释掉startOCR()>运行trainData() 启动识别>注释掉trainData()>运行startOCR()
     */

    /*
     * 验证码识别入口
     */
    public static void startOCR() throws Exception {
        int sameCount = 0;    // 重复验证码计数
        FileUtil.createDIR(RobConfiguration.IMG_PNG_TEST);
        FileUtil.createDIR(RobConfiguration.IMG_PNG_TEST_RE);
        for (int i = 0; i < 1000; i++) {
            robClass.logger.info("Code" + i + ".png");
            String resultStr = getAllOcr(RobConfiguration.IMG_PNG_TEST + "Code" + i + ".png");
            robClass.logger.info(i + ".png = " + resultStr);
            File source = new File(RobConfiguration.IMG_PNG_TEST + "Code" + i + ".png");
            File dest = new File(RobConfiguration.IMG_PNG_TEST_RE + resultStr + ".png");
            if (dest.exists()) {
                sameCount++;
            } else {
                Files.copy(source.toPath(), dest.toPath());
            }
        }
        robClass.logger.info("Same Result=" + sameCount);
    }

    /*
     * 生成字模入口
     */
    public static void trainData() throws Exception {
        FileUtil.createDIR(RobConfiguration.IMG_PNG_1K_ROOT);
        File dir = new File(RobConfiguration.IMG_PNG_1K_ROOT);
        File[] files = dir.listFiles();
        for (File file : files) {
            BufferedImage img = removeBackgroud(RobConfiguration.IMG_PNG_1K + file.getName());
            List<BufferedImage> listImg = splitImage(img);
            if (listImg.size() == 4) {
                for (int j = 0; j < listImg.size(); j++) {
                    ImageIO.write(listImg.get(j), "PNG",
                            new File(RobConfiguration.TRAIN + file.getName().charAt(j) + "-" + (index++) + ".png"));

                    robClass.logger.info(file.getName() + "\t" + file.getName().charAt(j) + "-" + (index++) + ".png");
                }
            }
        }
    }

    public static Map<BufferedImage, String> loadTrainData() throws Exception {
        FileUtil.createDIR(RobConfiguration.TRAIN_ROOT);
        if (trainMap == null) {
            Map<BufferedImage, String> map = new HashMap<BufferedImage, String>();
            File dir = new File(RobConfiguration.TRAIN_ROOT);
            File[] files = dir.listFiles();
            for (File file : files) {
//                robClass.logger.info(file.getName().charAt(0));
                map.put(ImageIO.read(file), file.getName().charAt(0) + "");
            }
            trainMap = map;
        }
        return trainMap;
    }

    /*
     * 获得所有验证码图片路径
     */
    public static String getAllOcr(String file) throws Exception {
        BufferedImage img = removeBackgroud(file);
        if (img == null) {
            return "#";
        }
        List<BufferedImage> listImg = splitImage(img);
        Map<BufferedImage, String> map = loadTrainData();
        String result = "";
        for (BufferedImage bIMG : listImg) {
            result += getSingleCharOcr(bIMG, map);
        }
        return result;
    }

    /*
     * 去除验证码背景并二值化
     */
    public static BufferedImage removeBackgroud(String picFile) throws Exception {
        try {


            File imgFile = new File(picFile);

            if (imgFile == null || !imgFile.exists()) {
                return null;
            }

            BufferedImage img = ImageIO.read(imgFile);
            if (img == null) {
                return null;
            }
            int width = img.getWidth();
            int height = img.getHeight();
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    if (isBlue(img.getRGB(x, y)) == 1) {
                        img.setRGB(x, y, Color.BLACK.getRGB());
                    } else {
                        img.setRGB(x, y, Color.WHITE.getRGB());
                    }
                }
            }
            return img;
        } catch (Exception e) {
            return null;
        }

    }

    public static int isBlue(int colorInt) {
        Color color = new Color(colorInt);
        int rgb = color.getRed() + color.getGreen() + color.getBlue();
        if (rgb == 153) {
            return 1;
        }
        return 0;
    }

    public static int isBlack(int colorInt) {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue() <= 100) {
            return 1;
        }
        return 0;
    }

    public static int isWhite(int colorInt) {
        Color color = new Color(colorInt);
        if (color.getRed() + color.getGreen() + color.getBlue() > 600) {
            return 1;
        }
        return 0;
    }

    /*
     * 切割验证码图片
     */
    public static List<BufferedImage> splitImage(BufferedImage img) throws Exception {
        List<BufferedImage> subImgs = new ArrayList<BufferedImage>();
        subImgs.add(img.getSubimage(5, 0, 12, 23));
        subImgs.add(img.getSubimage(17, 0, 12, 23));
        subImgs.add(img.getSubimage(29, 0, 12, 23));
        subImgs.add(img.getSubimage(41, 0, 12, 23));
        return subImgs;
    }

    /*
     * 识别切割的单个字符
     */
    public static String getSingleCharOcr(BufferedImage img, Map<BufferedImage, String> map) {
        String result = "#";
        int width = img.getWidth();
        int height = img.getHeight();
        int min = width * height;
        for (BufferedImage bi : map.keySet()) {
            int count = 0;
            if (Math.abs(bi.getWidth() - width) > 2)
                continue;
            int widthmin = width < bi.getWidth() ? width : bi.getWidth();
            int heightmin = height < bi.getHeight() ? height : bi.getHeight();
            Label1:
            for (int x = 0; x < widthmin; ++x) {
                for (int y = 0; y < heightmin; ++y) {
                    if (isBlack(img.getRGB(x, y)) != isBlack(bi.getRGB(x, y))) {
                        count++;
                        if (count >= min)
                            break Label1;
                    }
                }
            }
            if (count < min) {
                min = count;
                result = map.get(bi);
            }
        }
        return result;
    }

    public static void downloadCode(int i) {
        String SECRETCODE_URL = RobConfiguration.SECRETCODE_URL;
        HttpGet secretCodeGet = new HttpGet(SECRETCODE_URL);
        CloseableHttpClient client = HttpClients.createDefault();
        CloseableHttpResponse responseSecret = null;
        try {
            responseSecret = client.execute(secretCodeGet);
        } catch (IOException e) {
            e.printStackTrace();
        }
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(new File(RobConfiguration.IMG_1K + "Code" + i + ".gif"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        try {
            responseSecret.getEntity().writeTo(fileOutputStream);
            fileOutputStream.close();
            responseSecret.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        robClass.logger.info("Code" + i + ".gif");
    }

    /*
     * 获取验证码 并且 返回 Cookie
     * */
    //登录应该分 快速登录（1秒1次） 中速登录（5秒1次） 慢速登录（10秒） 极慢速登录(15秒1次) 以及5次随机数超时(1秒-50秒)发包
    public static void downloadCode(String cookie) {
        if (StringUtils.isBlank(cookie)) {
            return;
        }
        int timeout = 3;

        HttpResponse secretResponse;
        FileOutputStream fileOutputStream = null;
        FileUtil.createDIR(RobConfiguration.IMG_ACCOUNT_CODE);
        String SECRETCODE_URL = RobConfiguration.SECRETCODE_URL;
        try {
            HttpGet secretCodeGet = new HttpGet(SECRETCODE_URL);
            secretCodeGet.addHeader("Connection", "keep-alive");
            if (RobConfiguration.ROB_CHANNEL.equals(EnumRobChannel.UNDERGRADUATE)) {
                secretCodeGet.addHeader("Host", "192.168.170.253");
                secretCodeGet.addHeader("Cookie", cookie);
                secretCodeGet.addHeader("Referer", RobConfiguration.URL);
                secretCodeGet.addHeader("User-Agent","User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Mobile Safari/537.36 Edg/92.0.902.84");
            } else if (RobConfiguration.ROB_CHANNEL.equals(EnumRobChannel.SPECIALITY)) {
                secretCodeGet.addHeader("Cookie", cookie);
                secretCodeGet.addHeader("Referer", RobConfiguration.URL + RobConfiguration.DEFAULT_2);
            }
//            robClass.logger.info("SECRETCODE_URL：" + SECRETCODE_URL);
//            robClass.logger.info("downloadCode:cookie"+cookie);
//            robClass.logger.info("downloadCode:secretCodeGet"+secretCodeGet.toString());
            RequestConfig reqConfig = RequestConfig.custom().setConnectionRequestTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build();
            CloseableHttpAsyncClient client = HttpAsyncClients.custom().setDefaultRequestConfig(reqConfig).build();
            client.start();
            final long sendPackageTime = System.currentTimeMillis();
            Future<HttpResponse> future = client.execute(secretCodeGet, new FutureCallback<HttpResponse>() {
                @Override
                public void completed(HttpResponse result) {
                    long recPackageTime = System.currentTimeMillis();
//                    robClass.logger.info("回调消耗时间:" + (recPackageTime - sendPackageTime));
                }

                @Override
                public void failed(Exception ex) {
                    robClass.logger.info(ex.getMessage());
                }

                @Override
                public void cancelled() {
                    robClass.logger.info("下载验证码线程取消");
                }
            });
            secretResponse = future.get();
            client.close();
            try {
                File file;
                cookie = cookie.split("=")[1].split(";")[0];
                file = new File(RobConfiguration.IMG_ACCOUNT_CODE + "codebetterLogin_" + cookie + ".gif");
                fileOutputStream = new FileOutputStream(file);

            } catch (FileNotFoundException e) {
                e.printStackTrace();
                fileOutputStream.close();
            }
            try {
                secretResponse.getEntity().writeTo(fileOutputStream);

                fileOutputStream.close();
                gif2png(RobConfiguration.IMG_ACCOUNT_CODE + "codebetterLogin_" + cookie + ".gif", RobConfiguration.IMG_ACCOUNT_CODE + "codebetterLogin_" + cookie + ".png");

            } catch (IOException e) {
                e.printStackTrace();
                fileOutputStream.close();
            } finally {
                fileOutputStream.close();
            }
        } catch (InterruptedException e) {
            robClass.logger.info("downloadCode: 线程被打断");
            e.printStackTrace();
        } catch (ExecutionException e) {
            robClass.logger.info("downloadCode: 网络异常");
//            e.printStackTrace();
        } catch (IOException e) {
            robClass.logger.info("downloadCode: I/O异常");
//            e.printStackTrace();
        }
    }

    public static void gif2png(String src, String dst) {
        OutputStream out = null;
            /*
            * javax.imageio.IIOException: I/O error reading image!
	at java.desktop/com.sun.imageio.plugins.gif.GIFImageReader.read(GIFImageReader.java:1021)
	at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1468)
	at java.desktop/javax.imageio.ImageIO.read(ImageIO.java:1315)
	at checkcode.ImagePreProcess.gif2png(ImagePreProcess.java:320)
	at checkcode.ImagePreProcess.downloadCode(ImagePreProcess.java:296)
	at http.BasicRequest._login(BasicRequest.java:550)
	at user.impl.BetterUser._login(BetterUser.java:49)
	at user.impl.robUser.RobUser.login(RobUser.java:123)
	at robber.RobberUtil$1.run(RobberUtil.java:23)
	* */

        /**todo 注意同时读写情况报错*/
        try {
            try {
                out = new FileOutputStream(dst);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            ImageIO.write(ImageIO.read(new File(src)), "png", out);
        } catch (IIOException e) {

        } catch (IOException e) {
//            e.printStackTrace();
        } catch (Exception e) {

        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static String downloadCode() {
        int timeout = 3;
        String cookie;
        HttpResponse secretResponse;
        FileUtil.createDIR(RobConfiguration.IMG_ACCOUNT_CODE);
        String SECRETCODE_URL = RobConfiguration.SECRETCODE_URL;
        robClass.logger.info("SECRETCODE_URL:" + SECRETCODE_URL);
        try {
            HttpGet secretCodeGet = new HttpGet(SECRETCODE_URL);
            RequestConfig reqConfig = RequestConfig.custom().setConnectionRequestTimeout(timeout * 1000).setConnectTimeout(timeout * 1000).setSocketTimeout(timeout * 1000).build();
            CloseableHttpAsyncClient client = HttpAsyncClients.custom().setDefaultRequestConfig(reqConfig).build();
            client.start();
            final long sendPackageTime = System.currentTimeMillis();
            Future<HttpResponse> future = client.execute(secretCodeGet, new FutureCallback<HttpResponse>() {
                @Override
                public void completed(HttpResponse result) {
                    long recPackageTime = System.currentTimeMillis();
//                    robClass.logger.info("回调消耗时间:" + (recPackageTime - sendPackageTime));
                }

                @Override
                public void failed(Exception ex) {
                    robClass.logger.info(ex.getMessage());
                }

                @Override
                public void cancelled() {
                    robClass.logger.info("下载验证码线程取消");
                }
            });
            secretResponse = future.get();
            cookie = secretResponse.getFirstHeader("Set-Cookie").getValue();
            String fileName = cookie.split("=")[1].split(";")[0];
            client.close();

            FileOutputStream fileOutputStream = null;
            File file;
            try {
                file = new File(RobConfiguration.IMG_ACCOUNT_CODE + "codebetterLogin_" + fileName + ".gif");
                fileOutputStream = new FileOutputStream(file);

            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            try {
                secretResponse.getEntity().writeTo(fileOutputStream);
                fileOutputStream.close();
                gif2png(RobConfiguration.IMG_ACCOUNT_CODE + "codebetterLogin_" + fileName + ".gif", RobConfiguration.IMG_ACCOUNT_CODE + "codebetterLogin_" + fileName + ".png");
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                fileOutputStream.close();
            }

        } catch (SocketException e) {
            robClass.logger.info("获取验证码超时");
            return null;
        } catch (ConnectTimeoutException e) {
            robClass.logger.info("建立验证码链接超时");
            return null;
        } catch (ClientProtocolException e) {
            e.printStackTrace();
            return null;
        } catch (InterruptedException e) {
            e.printStackTrace();
            return null;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return null;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return cookie;
    }
}